home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / C / Applications / Rumor Mill / main.cp < prev    next >
Encoding:
Text File  |  1995-12-07  |  22.2 KB  |  915 lines  |  [TEXT/CWIE]

  1. /*
  2. ********************************************************************************
  3. **
  4. ** File: main.cp
  5. **
  6. ** Description:
  7. **
  8. **    This is the code for Rumor Mill, a simple application that allows users
  9. **    in the same zone to send "rumors" to each other.  The app registers itself
  10. **    via NBP, and then sends DDP packets to other Rumor Mill applications
  11. **    on the net when the user enters a new rumor and presses the "send" button
  12. **    or presses the <Return> key.
  13. **
  14. **    A DDP socket listener is installed by the application.  The socket listener
  15. **    was taken directly from Inside Macintosh: Networking.
  16. **
  17. ** Author: Cary A. Farrier, cary@laserpoint.com, <http://www.laserpoint.com>
  18. **
  19. ** Functions:
  20. **
  21. **    main();                            - program entry
  22. **    InitAppleTalk();                - network driver initialization
  23. **
  24. **    CRumorMillApp::
  25. **        CRumorMillApp();            - c'tor
  26. **        ~CRumorMillApp();            - d'tor
  27. **        StartUp();                    - pre-execution init
  28. **        ObeyCommand();                - command execution
  29. **        FindCommandStatus();        - menu state checking
  30. **        ListenToMessage();            - message execution
  31. **        NetStart();                    - application network startup
  32. **        FindOtherMills();            - search for other Rumor Mills
  33. **        RegisterName();                - register app on network
  34. **        UnregisterName();            - unregister app on network
  35. **        NetStop();                    - shutdown app network connection
  36. **        SendRumor();                - send a rumor to other rumor mills
  37. **        DisplayRumor();                - display queued rumors received
  38. **        ProcessNextEvent();            - pull incoming DDP packets from queues
  39. **        SetStatusMessage();            - set status area message
  40. **        GetStatusMessage();            - get status area message
  41. **        KeyFilterFunction();        - check for <Return> key
  42. **
  43. ********************************************************************************
  44. **
  45. ** Revision History:
  46. **
  47. ** 12/06/97 Farrier    Created.
  48. **
  49. ********************************************************************************
  50. */
  51. #include "main.h"
  52.  
  53. #include <LGrowZone.h>
  54. #include <LWindow.h>
  55. #include <PP_Messages.h>
  56. #include <PP_Resources.h>
  57. #include <PPobClasses.h>
  58. #include <UDrawingState.h>
  59. #include <UMemoryMgr.h>
  60. #include <URegistrar.h>
  61. #include <LEditField.h>
  62. #include <LGroupBox.h>
  63. #include <UKeyFilters.h>
  64.  
  65. #include <AppleTalk.h>
  66.  
  67. #include <string.h>
  68.  
  69. /*
  70. ********************************************************************************
  71. ** externals from listener.asm
  72. ********************************************************************************
  73. */
  74. extern pascal OSErr SL_InitSktListener( QHdr *inFreeQueue, QHdr *inUsedQueue );
  75. extern pascal void SL_TheListener( void );
  76.  
  77. /*
  78. ********************************************************************************
  79. ** constants
  80. ********************************************************************************
  81. */
  82. #define kDDPMaxData            586
  83. #define kNumPacketBuffers    10
  84. #define kMaxMessages         25
  85.  
  86. /*
  87. ********************************************************************************
  88. ** structures
  89. ********************************************************************************
  90. */
  91. struct Message {
  92.     QElemPtr    qLink;
  93.     Boolean        used;
  94.     Str255        message;
  95. };
  96. typedef struct Message Message, *MessagePtr, **MessageHandle;
  97.  
  98. struct PacketBuffer {
  99.     QElemPtr    qLink;
  100.     short        qType;
  101.     short        buffer_Type;
  102.     short        buffer_NodeID;
  103.     AddrBlock    buffer_Address;
  104.     short        buffer_Hops;
  105.     short        buffer_ActCount;
  106.     OSErr        buffer_CheckSum;
  107.     char        buffer_Data[kDDPMaxData];
  108.     };
  109. typedef struct PacketBuffer PacketBuffer, *PacketBufferPtr, **PacketBufferHandle;
  110.  
  111. /*
  112. ********************************************************************************
  113. ** globals
  114. ********************************************************************************
  115. */
  116. short gMPPDriverRef;
  117. NamesTableEntry gMyNTE;
  118. Str32 gMyName;
  119. UInt8 gTheSocket;
  120. PacketBuffer gPacketBuffers[kNumPacketBuffers];
  121. QHdr gFreePacketQueue, gUsedPacketQueue, gMessageQueue;
  122. Uint8 gOtherRumorMills[256];
  123. Message gMessageStorage[kMaxMessages];
  124. CRumorMillApp *gApp;
  125.  
  126. /*
  127. ********************************************************************************
  128. ** resource ids
  129. ********************************************************************************
  130. */
  131. #define window_Sample        128
  132. #define alert_Error            129
  133.  
  134. /*
  135. ********************************************************************************
  136. ** pane ids
  137. ********************************************************************************
  138. */
  139. #define button_Send            1
  140. #define ledit_IncomingRumor    10
  141. #define ledit_NewRumor        11
  142. #define lcapt_Status        12
  143.  
  144. /*
  145. ********************************************************************************
  146. ** message ids
  147. ********************************************************************************
  148. */
  149. #define msg_SendRumor        1000
  150.  
  151. /*
  152. ********************************************************************************
  153. ** other data
  154. ********************************************************************************
  155. */
  156. const Str32 str32_RumorMill = "\pRumorMill";
  157.  
  158. /*
  159. ********************************************************************************
  160. ** prototypes
  161. ********************************************************************************
  162. */
  163. Boolean InitAppleTalk( void );
  164.  
  165. /*
  166. ********************************************************************************
  167. **
  168. ** Function: main()
  169. **
  170. ** Description:
  171. **
  172. **    Program entry point.  Sets up the heap and runs the application object.
  173. **
  174. ********************************************************************************
  175. */
  176. void main(void)
  177. {
  178.                                     // Set Debugging options
  179.     SetDebugThrow_(debugAction_Alert);
  180.     SetDebugSignal_(debugAction_Alert);
  181.  
  182.     InitializeHeap(3);                // Initialize Memory Manager
  183.                                     // Parameter is number of Master Pointer
  184.                                     //   blocks to allocate
  185.     
  186.                                     // Initialize standard Toolbox managers
  187.     UQDGlobals::InitializeToolbox(&qd);
  188.     
  189.     new LGrowZone(20000);            // Install a GrowZone function to catch
  190.                                     //    low memory situations.
  191.  
  192.     // initialize appletalk
  193.     if( FALSE == InitAppleTalk() )
  194.         return;
  195.     
  196.     CRumorMillApp    theApp;            // replace this with your App type
  197.     gApp = &theApp;
  198.     theApp.Run();
  199. }
  200.  
  201. /*
  202. ********************************************************************************
  203. **
  204. ** Function: InitAppleTalk()
  205. **
  206. ** Description:
  207. **
  208. **    Checks for the correct version of AppleTalk (phase 2), and then opens
  209. **    the low level AppleTalk driver, ".MPP", that contains DDP.
  210. **
  211. ********************************************************************************
  212. */
  213. Boolean
  214. InitAppleTalk( void )
  215. {
  216.     OSErr theErr;
  217.     SysEnvRec theEnvirons;
  218.  
  219.     // check for appletalk phase 2
  220.     theErr = SysEnvirons(curSysEnvVers, &theEnvirons);
  221.     if(theEnvirons.atDrvrVersNum <= 52)
  222.     {
  223.         ::ParamText("\pThis program requires a later version of AppleTalk", NULL, NULL, NULL);
  224.         ::StopAlert( alert_Error, NULL );
  225.         return FALSE;
  226.     }
  227.     
  228.     // open the .MPP driver
  229.     theErr = OpenDriver( "\p.MPP", &gMPPDriverRef );
  230.     if ( noErr != theErr )
  231.     {
  232.         ::ParamText("\pError opening .MPP driver", NULL, NULL, NULL);
  233.         ::StopAlert( alert_Error, NULL );
  234.         return FALSE;
  235.     }
  236.  
  237.     return TRUE;
  238. }
  239.  
  240. /*
  241. ********************************************************************************
  242. **
  243. ** Function: CRumorMillApp::CRumorMillApp()
  244. **
  245. ** Description:
  246. **
  247. **    Constructor.  Registers PowerPlant classes.
  248. **
  249. ********************************************************************************
  250. */
  251. CRumorMillApp::CRumorMillApp()
  252. {
  253.     // Register functions to create core PowerPlant classes
  254.     
  255.     RegisterAllPPClasses();
  256.     URegistrar::RegisterClass(LGroupBox::class_ID,        LGroupBox::CreateGroupBoxStream);
  257. }
  258.  
  259. /*
  260. ********************************************************************************
  261. **
  262. ** Function: CRumorMillApp::~CRumorMillApp()
  263. **
  264. ** Description:
  265. **
  266. **    Destructor.  Closes down network connection.
  267. **
  268. ********************************************************************************
  269. */
  270. CRumorMillApp::~CRumorMillApp()
  271. {
  272.     NetStop();
  273. }
  274.  
  275. /*
  276. ********************************************************************************
  277. **
  278. ** Function: CRumorMillApp::Startup()
  279. **
  280. ** Description:
  281. **
  282. **    Called after application object has been created and initialized, but
  283. **    before it begins the main event loop.  A new window is created here and
  284. **    the network connection is started.
  285. **
  286. ********************************************************************************
  287. */
  288. void
  289. CRumorMillApp::StartUp()
  290. {
  291.     ObeyCommand(cmd_New, nil);
  292.     
  293.     NetStart();
  294. }
  295.  
  296. /*
  297. ********************************************************************************
  298. **
  299. ** Function: CRumorMillApp::ObeyCommand()
  300. **
  301. ** Description:
  302. **
  303. **    Command handler for the application class.  Currently only responds to
  304. **    the cmd_New command to create a new window.  For each window created,
  305. **    the application is setup as a listener for the "send" button and a key
  306. **    filter is installed for the edit control to detect the <Return> key
  307. **    being pressed (so that the rumor can be sent out).
  308. **
  309. ********************************************************************************
  310. */
  311. Boolean
  312. CRumorMillApp::ObeyCommand(
  313.     CommandT    inCommand,
  314.     void        *ioParam)
  315. {
  316.     Boolean        cmdHandled = true;
  317.     LStdButton    *theButton;
  318.     LWindow *theWindow;
  319.     LEditField *theEdit;
  320.     
  321.     switch (inCommand) {
  322.     
  323.         case cmd_New:
  324.             theWindow = LWindow::CreateWindow(window_Sample, this);    
  325.             if( theWindow )
  326.             {
  327.                 theWindow->Show();
  328.                 theButton = (LStdButton *)theWindow->FindPaneByID( button_Send );
  329.                 if( theButton )
  330.                     theButton->AddListener( this );
  331.                     
  332.                 theEdit = (LEditField *)theWindow->FindPaneByID( ledit_NewRumor );
  333.                 if( theEdit )
  334.                 {
  335.                     theEdit->SetKeyFilter( CRumorMillApp::KeyFilterFunction );
  336.                     LCommander::SwitchTarget( theEdit );
  337.                 }
  338.             }
  339.             break;
  340.  
  341.         default:
  342.             cmdHandled = LApplication::ObeyCommand(inCommand, ioParam);
  343.             break;
  344.     }
  345.     
  346.     return cmdHandled;
  347. }
  348.  
  349. /*
  350. ********************************************************************************
  351. **
  352. ** Function: CRumorMillApp::FindCommandStatus()
  353. **
  354. ** Description:
  355. **
  356. **    Returns the enabled/disabled/checked state of the menus.
  357. **
  358. ********************************************************************************
  359. */
  360. void
  361. CRumorMillApp::FindCommandStatus(
  362.     CommandT    inCommand,
  363.     Boolean        &outEnabled,
  364.     Boolean        &outUsesMark,
  365.     Char16        &outMark,
  366.     Str255        outName)
  367. {
  368.  
  369.     switch (inCommand) {
  370.     
  371.         // Return menu item status according to command messages.
  372.         // Any that you don't handle will be passed to LApplication
  373.  
  374.         case cmd_New:
  375.             outEnabled = true;
  376.             break;
  377.  
  378.         default:
  379.             LApplication::FindCommandStatus(inCommand, outEnabled,
  380.                                                 outUsesMark, outMark, outName);
  381.             break;
  382.     }
  383. }
  384.  
  385. /*
  386. ********************************************************************************
  387. **
  388. ** Function: CRumorMillApp::ListenToMessage()
  389. **
  390. ** Description:
  391. **
  392. **    Message handler.  Responds to the "send rumor" message by sending the
  393. **    rumor out over the network.
  394. **
  395. ********************************************************************************
  396. */
  397. void
  398. CRumorMillApp::ListenToMessage(
  399.     MessageT inMessage,
  400.     void *ioParam
  401. )
  402. {
  403.     LWindow        *theWindow = NULL;
  404.     LEditField    *theEdit;
  405.     Str255 theText;
  406.  
  407.     switch( inMessage )
  408.     {
  409.         case msg_SendRumor:
  410.             theWindow = LWindow::FetchWindowObject( FrontWindow() );
  411.             if( theWindow )
  412.             {
  413.                 theEdit = (LEditField *)theWindow->FindPaneByID( ledit_NewRumor );
  414.                 if( theEdit )
  415.                 {
  416.                     theEdit->GetDescriptor( theText );
  417.                     if( theText[0] )
  418.                         SendRumor( theText );
  419.                 }
  420.             }        
  421.             break;
  422.             
  423.         default:
  424.             break;
  425.     }
  426. }
  427.  
  428. /*
  429. ********************************************************************************
  430. **
  431. ** Function: CRumorMillApp::NetStart()
  432. **
  433. ** Description:
  434. **
  435. **    Application specific network startup.  Creates a socket listener with
  436. **    buffer queues, opens a socket, and registers the application via NBP.
  437. **
  438. ********************************************************************************
  439. */
  440. Boolean
  441. CRumorMillApp::NetStart( void )
  442. {
  443.     OSErr theErr;
  444.     MPPParamBlock thePB;
  445.     Byte theSocket;
  446.     int i;
  447.     
  448.     // initialize socket listener
  449.     for( i = 0; i < kNumPacketBuffers; i++ )
  450.         Enqueue( (QElem *)&gPacketBuffers[i], &gFreePacketQueue );
  451.     theErr = SL_InitSktListener( &gFreePacketQueue, &gUsedPacketQueue );
  452.     if( theErr )
  453.     {
  454.         ::ParamText("\pError initializing socket listener", NULL, NULL, NULL);
  455.         ::StopAlert( alert_Error, NULL );
  456.         return false;
  457.     }
  458.     
  459.     // open socket
  460.     memset( &thePB, 0, sizeof( thePB ) );
  461.     thePB.DDPlistener = SL_TheListener;
  462.     theErr = POpenSkt( &thePB, FALSE );
  463.     if( noErr != theErr )
  464.     {
  465.         ::ParamText("\pError opening socket", NULL, NULL, NULL);
  466.         ::StopAlert( alert_Error, NULL );
  467.         return false;
  468.     }
  469.     gTheSocket = thePB.DDPsocket;
  470.     
  471.     // register via nbp
  472.     if( FALSE == RegisterName( gTheSocket ) )
  473.         return FALSE;
  474.         
  475.     return TRUE;
  476. }
  477.  
  478. /*
  479. ********************************************************************************
  480. **
  481. ** Function: CRumorMillApp::FindOtherMills()
  482. **
  483. ** Description:
  484. **
  485. **    Searches for other Rumor Mill applications via NBP.  This takes a few
  486. **    seconds so the watch cursor is displayed.
  487. **
  488. ********************************************************************************
  489. */
  490. Boolean
  491. CRumorMillApp::FindOtherMills( void )
  492. {
  493.     OSErr theErr;
  494.     MPPParamBlock thePB;
  495.     int i;
  496.     char theLookupBuffer[256*104];
  497.     NamesTableEntry theEntity;
  498.     EntityName theName;
  499.     AddrBlock theAddress;
  500.     Str255 theOldStatus;
  501.     
  502.     GetStatusMessage( theOldStatus );
  503.     SetStatusMessage( "\pLooking for someone to gossip with…" );
  504.  
  505.     SetCursor( *(GetCursor( watchCursor )) );
  506.     
  507.     // find other rumor mills in this zone & add to list
  508.     memset( &theEntity, 0, sizeof( theEntity ) );
  509.     NBPSetEntity( (Ptr)&theEntity.nt.entityData,
  510.         (ConstStr32Param)"\p=", (ConstStr32Param)str32_RumorMill, "\p*" );
  511.         
  512.     memset( &thePB, 0, sizeof( thePB ) );
  513.     thePB.NBP.ioRefNum = gMPPDriverRef;
  514.     thePB.NBPinterval = 7;
  515.     thePB.NBPcount = 5;
  516.     thePB.NBPentityPtr = (Ptr)&theEntity.nt.entityData;
  517.     thePB.NBPretBuffPtr = theLookupBuffer;
  518.     thePB.NBPretBuffSize = sizeof(theLookupBuffer);
  519.     thePB.NBPmaxToGet = 256;
  520.     theErr = PLookupName( &thePB, false );
  521.     if( theErr )
  522.     {
  523.         ::ParamText("\pError from PLookupName", NULL, NULL, NULL);
  524.         ::StopAlert( alert_Error, NULL );
  525.         SetStatusMessage( theOldStatus );
  526.         SetCursor( &qd.arrow );
  527.         return false;
  528.     }
  529.     if( thePB.NBPnumGotten > 0 )
  530.     {
  531.         memset( gOtherRumorMills, 0, sizeof( gOtherRumorMills ) );
  532.         for( i = 1; i <= thePB.NBPnumGotten; i++ )
  533.         {
  534.             theErr = NBPExtract( theLookupBuffer, thePB.NBPnumGotten, i,
  535.                 &theName, &theAddress );
  536.             if( noErr == theErr )
  537.             {
  538.                 gOtherRumorMills[theAddress.aNode] = theAddress.aSocket;
  539.             }
  540.         }
  541.     }
  542.     
  543.     SetStatusMessage( theOldStatus );
  544.     SetCursor( &qd.arrow );
  545.     
  546.     return TRUE;
  547. }
  548.  
  549. /*
  550. ********************************************************************************
  551. **
  552. ** Function: CRumorMillApp::RegisterName()
  553. **
  554. ** Description:
  555. **
  556. **    Registers the application on the network using NBP.  The user's name
  557. **    (entered in the Sharing Setup control panel) is used as the name.
  558. **
  559. ********************************************************************************
  560. */
  561. Boolean
  562. CRumorMillApp::RegisterName( 
  563.     UInt8 inSocket
  564. )
  565. {
  566.     MPPParamBlock pb;
  567.     StringHandle userName;
  568.     
  569.     userName = GetString(-16096);
  570.  
  571.     gMyName[0] = 0;
  572.     if (userName != nil)
  573.     {
  574.         if (*userName[0] != 0)
  575.             BlockMove(*userName,&gMyName,*userName[0]+1);
  576.     }
  577.  
  578.     NBPSetNTE( (Ptr)&gMyNTE, (ConstStr32Param)&gMyName, (ConstStr32Param)str32_RumorMill,
  579.         "\p*", inSocket );
  580.  
  581.     pb.NBPinterval = 2;
  582.     pb.NBPcount = 3;
  583.     pb.NBPentityPtr = (Ptr)&gMyNTE;
  584.     pb.NBPverifyFlag = 1;
  585.     if ( PRegisterName( &pb,false ) != noErr )
  586.     {
  587.         ::ParamText("\pError registering name", NULL, NULL, NULL);
  588.         ::StopAlert( alert_Error, NULL );
  589.         return false;
  590.     }
  591.     
  592.     return true;
  593. }
  594.  
  595. /*
  596. ********************************************************************************
  597. **
  598. ** Function: CRumorMillApp::UnregisterName()
  599. **
  600. ** Description:
  601. **
  602. **    Removes our registered name from the network.
  603. **
  604. ********************************************************************************
  605. */
  606. Boolean
  607. CRumorMillApp::UnregisterName( void )
  608. {
  609.     MPPParamBlock pb;
  610.     OSErr err;
  611.  
  612.     pb.NBPentityPtr = (Ptr)&gMyNTE.nt.entityData;
  613.     err = PRemoveName(&pb, false);
  614.  
  615.     return TRUE;
  616. }
  617.  
  618. /*
  619. ********************************************************************************
  620. **
  621. ** Function: CRumorMillApp::NetStop()
  622. **
  623. ** Description:
  624. **
  625. **    Closes down our socket.
  626. **
  627. ********************************************************************************
  628. */
  629. Boolean
  630. CRumorMillApp::NetStop( void )
  631. {
  632.     OSErr theErr;
  633.     MPPParamBlock thePB;
  634.     Byte theSocket;
  635.     
  636.     // unregister via nbp
  637.     UnregisterName();
  638.     
  639.     // close socket
  640.     memset( &thePB, 0, sizeof( thePB ) );
  641.     thePB.DDPsocket = gTheSocket;
  642.     theErr = PCloseSkt(&thePB, FALSE);
  643.     if( noErr != theErr )
  644.     {
  645.         ::ParamText("\pError closing socket", NULL, NULL, NULL);
  646.         ::StopAlert( alert_Error, NULL );
  647.     }
  648.  
  649.     return TRUE;
  650. }
  651.  
  652. /*
  653. ********************************************************************************
  654. **
  655. ** Function: CRumorMillApp::SendRumor()
  656. **
  657. ** Description:
  658. **
  659. **    Scans the net for other Rumor Mills, then sends a rumor to the other
  660. **    mills found.
  661. **
  662. ********************************************************************************
  663. */
  664. Boolean
  665. CRumorMillApp::SendRumor(
  666.     Str255 inRumorText
  667. )
  668. {
  669.     int i;
  670.     MPPParamBlock thePB;
  671.     char theWDS[14], theHeader[17];
  672.     AddrBlock theAddress;
  673.     OSErr theErr;
  674.     
  675.     // find all the mills out there
  676.     FindOtherMills();
  677.     
  678.     // find first known mill
  679.     for( i = 0; i < 256; i++ )
  680.         if( gOtherRumorMills[i] )
  681.             break;
  682.  
  683.     // send to all known rumor mills
  684.     if( i < 256 )
  685.     {
  686.         for( /* empty */ ; i < 256; i++ )
  687.         {
  688.             if( gOtherRumorMills[i] )
  689.             {
  690.                 // setup address
  691.                 theAddress.aNet = 0;
  692.                 theAddress.aNode = i;
  693.                 theAddress.aSocket = gOtherRumorMills[i];
  694.                 
  695.                 // build wds
  696.                 BuildDDPwds( theWDS, theHeader, (char *)inRumorText, theAddress, 16, inRumorText[0]+1 );
  697.     
  698.                 // build paramblock
  699.                 memset( &thePB, 0, sizeof( thePB ) );
  700.                 thePB.DDPsocket = gTheSocket;
  701.                 thePB.DDPwdsPointer = (char *)&theWDS;
  702.                 
  703.                 // write data
  704.                 theErr = PWriteDDP(&thePB, FALSE);
  705.                 if( noErr != theErr )
  706.                 {
  707.                     ::ParamText("\pError writing to socket", NULL, NULL, NULL);
  708.                     ::StopAlert( alert_Error, NULL );
  709.                 }
  710.             }        
  711.         }
  712.     }
  713.     
  714.     return TRUE;
  715. }
  716.  
  717. /*
  718. ********************************************************************************
  719. **
  720. ** Function: CRumorMillApp::DisplayRumor()
  721. **
  722. ** Description:
  723. **
  724. **    This function is called from our main event processing loop, and every
  725. **    five seconds it will check to see if a rumor is available in the incoming
  726. **    rumor messages queue.  If so, it will display the new message.
  727. **
  728. ********************************************************************************
  729. */
  730. Boolean
  731. CRumorMillApp::DisplayRumor( void )
  732. {
  733.     static long theLastTime = 0;
  734.     long theCount;
  735.     MessagePtr theMessage;
  736.     LWindow *theWindow;
  737.     LEditField *theEdit;
  738.     
  739.     // display new messages every 5 seconds
  740.     theCount = TickCount();
  741.     if( theCount - theLastTime > 5*60 )
  742.     {
  743.         theLastTime = theCount;
  744.         if( gMessageQueue.qHead )
  745.         {
  746.             // extract message
  747.             theMessage = (MessagePtr)gMessageQueue.qHead;
  748.             Dequeue( (QElemPtr)theMessage, &gMessageQueue );
  749.  
  750.             // display message
  751.             theWindow = LWindow::FetchWindowObject( FrontWindow() );
  752.             if( theWindow )
  753.             {
  754.                 theEdit = (LEditField *)theWindow->FindPaneByID( ledit_IncomingRumor );
  755.                 if( theEdit )
  756.                     theEdit->SetDescriptor( theMessage->message );
  757.             }
  758.             
  759.             // make message available for reuse
  760.             theMessage->used = FALSE;
  761.         }
  762.     }
  763.  
  764.     return TRUE;
  765. }
  766.  
  767. /*
  768. ********************************************************************************
  769. **
  770. ** Function: CRumorMillApp::ProcessNextEvent()
  771. **
  772. ** Description:
  773. **
  774. **    Overrides the LApplication::ProcessNextEvent() so that we can pull incoming
  775. **    DDP packets out of the queue and move them into the rumor message queue
  776. **    for display.  Also calls DisplayRumor() before passing control to the
  777. **    real event handler in the base class.
  778. **
  779. ********************************************************************************
  780. */
  781. void
  782. CRumorMillApp::ProcessNextEvent()
  783. {
  784.     PacketBuffer *thePacket;
  785.     MessagePtr theMessage;
  786.     int i;
  787.     
  788.     // retrieve pending ddp packets
  789.     while( gUsedPacketQueue.qHead )
  790.     {
  791.         // retrieve the packet
  792.         thePacket = (PacketBufferPtr)gUsedPacketQueue.qHead;
  793.         
  794.         // find space for the new message, if available
  795.         // if no space we'll just ignore the packet
  796.         theMessage = gMessageStorage;
  797.         for( i = 0; i < kMaxMessages; i++ )
  798.             if( FALSE == theMessage->used )
  799.                 break;
  800.         if( i < kMaxMessages )
  801.         {
  802.             // mark message used
  803.             theMessage->used = TRUE;
  804.  
  805.             // copy the data
  806.             ::BlockMove( thePacket->buffer_Data, theMessage->message,
  807.                 thePacket->buffer_Data[0]+1 );
  808.             
  809.             // enqueue it
  810.             Enqueue( (QElemPtr)theMessage, &gMessageQueue );
  811.         }
  812.         // remove packet from used queue
  813.         Dequeue( (QElemPtr)thePacket, &gUsedPacketQueue );
  814.         
  815.         // add packet to free queue
  816.         Enqueue( (QElemPtr)thePacket, &gFreePacketQueue );
  817.     }
  818.     
  819.     // display any pending rumors
  820.     DisplayRumor();
  821.     
  822.     // let the base class process events normally now
  823.     LApplication::ProcessNextEvent();
  824. }
  825.  
  826. /*
  827. ********************************************************************************
  828. **
  829. ** Function: CRumorMillApp::SetStatusMessage()
  830. **
  831. ** Description:
  832. **
  833. **    Sets the status message area text.
  834. **
  835. ********************************************************************************
  836. */
  837. void
  838. CRumorMillApp::SetStatusMessage(
  839.     Str255 inStatus
  840. )
  841. {
  842.     LWindow *theWindow;
  843.     LCaption *theCaption;
  844.     
  845.     theWindow = LWindow::FetchWindowObject( FrontWindow() );
  846.     if( theWindow )
  847.     {
  848.         theCaption = (LCaption *)theWindow->FindPaneByID( lcapt_Status );
  849.         if( theCaption )
  850.         {
  851.             theCaption->SetDescriptor( inStatus );
  852.             theCaption->Draw( NULL );
  853.         }
  854.     }
  855. }
  856.  
  857. /*
  858. ********************************************************************************
  859. **
  860. ** Function: CRumorMillApp::GetStatusMessage()
  861. **
  862. ** Description:
  863. **
  864. **    Returns the status area text.
  865. **
  866. ********************************************************************************
  867. */
  868. void
  869. CRumorMillApp::GetStatusMessage(
  870.     Str255 outStatus
  871. )
  872. {
  873.     LWindow *theWindow;
  874.     LCaption *theCaption;
  875.     
  876.     theWindow = LWindow::FetchWindowObject( FrontWindow() );
  877.     if( theWindow )
  878.     {
  879.         theCaption = (LCaption *)theWindow->FindPaneByID( lcapt_Status );
  880.         if( theCaption )
  881.             theCaption->GetDescriptor( outStatus );
  882.     }
  883. }
  884.  
  885. /*
  886. ********************************************************************************
  887. **
  888. ** Function: CRumorMillApp::KeyFilterFunction()
  889. **
  890. ** Description:
  891. **
  892. **    This key filter is installed in the window's edit field so that when a
  893. **    <Return> is detected the text the user has entered will be sent out
  894. **    to the other rumor mills.
  895. **
  896. ********************************************************************************
  897. */
  898. EKeyStatus
  899. CRumorMillApp::KeyFilterFunction(
  900.     const EventRecord&    inKeyEvent
  901. )
  902. {
  903.     LWindow        *theWindow = NULL;
  904.     LEditField    *theEdit;
  905.     Str255 theText;
  906.  
  907.     // if the user presses the return key then send the message
  908.     if( (inKeyEvent.message & 0xFF) == '\r' )
  909.     {
  910.         gApp->ListenToMessage( msg_SendRumor, NULL );
  911.         return keyStatus_Ignore;
  912.     }
  913.     
  914.     return keyStatus_Input;
  915. }